﻿using CommonLib.Attr;
using CommonLib.Base;
using CommonLib.CodeEntity;
using CommonLib.Util;
using System.Text.RegularExpressions;

namespace Oracle.Lexer;

[Order(1)]
public partial class SelectLexer : BaseLexer
{
    public override bool Check()
        => Txt.StartsWith("SELECT ");

    protected override BaseCodeEntity Run()
    {
        var entity = new SelectCodeEntity();
        ConstUtil.SetSql(true, entity);
        var sel = Txt[6..].TrimEnd(';').Trim();
        var cols = sel.FullWord(" FROM");
        Columns(cols, entity);
        sel = sel.Replace(cols, "").Trim();
        sel = sel[4..].Trim();
        string tbls;
        if (!sel.Contains(" WHERE")) tbls = sel;
        else
        {
            tbls = sel.FullWord(" WHERE");
            sel = sel.Replace(tbls, "").Trim();
            if (sel.Contains("UNION "))
            {
                var union = sel[sel.IndexOf("UNION ")..].Trim();
                sel = sel[..sel.IndexOf("UNION ")].Trim();
                var ustr = union[..union.IndexOf(" SELECT")].Trim();
                entity.Ustr = ustr;
                entity.Union = SqlFactory.Create(union.Replace(ustr, "").Trim()).Execute();
            }
            if (sel.Contains(" GROUP"))
            {
                entity.End = sel[sel.IndexOf(" GROUP")..].Trim();
                sel = sel[..sel.IndexOf(" GROUP")].Trim();
            }
            if (sel.Contains(" ORDER"))
            {
                entity.End = sel[sel.IndexOf(" ORDER")..].Trim();
                sel = sel[..sel.IndexOf(" ORDER")].Trim();
            }
            Wheres(sel, entity);
        }
        Tables(tbls, entity);
        ConstUtil.SetSql(false, entity);
        return entity;
    }

    private void Columns(string cols, SelectCodeEntity entity)
    {
        var arr = cols.TrimSplit(",");
        var sub = new List<string>();
        //Column 断句
        for (int i = 0; i < arr.Length; i++)
        {
            var item = arr[i].Trim();
            while (!item.IsFull())
            {
                var next = arr[++i].Trim();
                item = $"{item},{next}";
            }
            sub.Add(item);
        }
        //Column 解析
        for (int i = 0; i < sub.Count; i++)
        {
            var item = sub[i];
            if (item.StartsWith("INTO"))
            {
                entity.Into = i;
                item = item[4..].Trim();
            }
            if (item.StartsWith("CASE "))
            {
                var end = item[(item.LastIndexOf("END") + 3)..].Trim();
                var str = item[..(item.LastIndexOf("END") + 3)].Trim();
                var obj = SqlFactory.Create(str).Execute();
                entity.Colums.Add(new SqlBlockCodeEntity(end, obj));
            }
            else
            {
                var tup = SubBlock(item);
                entity.Colums.Add(new SqlBlockCodeEntity(tup.Item1, tup.Item2));
            }
        }
    }

    private void Tables(string tbls, SelectCodeEntity entity)
    {
        var sub = new List<string>();
        var arr = tbls.TrimSplit(',');
        //Table 断句
        for (int i = 0; i < arr.Length; i++)
        {
            var item = arr[i].Trim();
            while (!item.IsFull())
            {
                var next = arr[++i].Trim();
                item = $"{item},{next}";
            }
            sub.Add(item);
        }
        //table 解析
        for (int i = 0; i < sub.Count; i++)
        {
            var item = sub[i];
            if (item.Contains(" JOIN"))
            {
                Joins(item, entity);
            }
            else
            {
                var tup = SubBlock(item);
                entity.Tables.Add(new SqlBlockCodeEntity(tup.Item1, tup.Item2));
            }
        }
    }

    private void Joins(string joins, SelectCodeEntity entity)
    {
        var arr = joins.TrimSplit(" JOIN ");
        var list = new List<string>();
        for (int i = 0; i < arr.Length; i++)
        {
            var item = arr[i];
            while (!item.IsFull())
            {
                item = $"{item} JOIN {arr[++i]}";
            }
            list.Add(item);
        }
        var joincode = new JoinCodeEntity();
        foreach (var item in list)
        {
            var join = JoinRegex().Match(item).Value.Trim();
            var tbl = item;
            if (string.IsNullOrWhiteSpace(join)) join = "JOIN";
            else
            {
                tbl = tbl.Replace(join, "");
                join = $"{join} JOIN";
            }
            joincode.Jstr.Add(join);
            if (tbl.Contains(" ON "))
            {
                var on = tbl;
                tbl = tbl.FullWord(" ON ");
                on = on.Replace(tbl, "").Trim();
                joincode.Cons.Add(SqlFactory.Create(on).Execute());
            }
            else joincode.Cons.Add(null!);
            var tup = SubBlock(tbl);
            joincode.TblCode.Add(new SqlBlockCodeEntity(tup.Item1, tup.Item2));
        }
        entity.Tables.Add(joincode);
    }

    private Tuple<string, BaseCodeEntity> SubBlock(string tbl)
    {
        var end = string.Empty;
        BaseCodeEntity obj;
        if (tbl.Contains('('))
        {
            var subsel = BracketRegex().Match(tbl).Result("$1").Trim();
            end = EndRegex().Replace(tbl, "").Trim();
            obj = SqlFactory.Create(subsel).Execute();
        }
        else if (tbl.Contains(' '))
        {
            var str = tbl[..tbl.IndexOf(" ")].Trim();
            end = tbl.Replace(str, "").Trim();
            obj = SqlFactory.Create(str).Execute();
        }
        else obj = SqlFactory.Create(tbl).Execute();
        return new Tuple<string, BaseCodeEntity>(end, obj);
    }

    private void Wheres(string wheres, SelectCodeEntity entity)
    {
        if (string.IsNullOrEmpty(wheres)) return;
        var arr = wheres.TrimSplit("AND");
        var sub = new List<string>();
        for (int i = 0; i < arr.Length; i++)
        {
            var item = arr[i];
            while (!item.IsFull())
            {
                var next = arr[++i].Trim();
                item = $"{item} AND {next}";
            }
            sub.Add(item);
        }

        for (int i = 0; i < sub.Count; i++)
        {
            var item = sub[i];
            if (!item.StartsWith("WHERE")) item = $"AND {item}";
            if (item.Contains("BETWEEN"))
            {
                item = $"{item} AND {sub[++i]}";
            }
            var obj = SqlFactory.Create(item).Execute();
            entity.Conditions.Add(obj);
        }
    }

    [GeneratedRegex("(LEFT|RIGHT|CROSS)?\\s(INNER|OUTER)?")]
    private static partial Regex JoinRegex();
    [GeneratedRegex("(.*\\))")]
    private static partial Regex BracketRegex();
    [GeneratedRegex(".*\\)")]
    private static partial Regex EndRegex();
}
